#include "viewer.hpp"
#include <iostream>
#include <GL/gl.h>
#include <GL/glu.h>

#define MIN_SCALE 0.2
#define MAX_SCALE 3
#define GAME_WIDTH 10
#define GAME_HEIGHT 24
#define MAX_ROTATE 60
#define SPEEDFACTOR 0.8
#define ACCUM_BOUND 250

Viewer::Viewer(): myGame(GAME_WIDTH,GAME_HEIGHT)
{
  Glib::RefPtr<Gdk::GL::Config> glconfig;

  // Ask for an OpenGL Setup with
  //  - red, green and blue component colour
  //  - a depth buffer to avoid things overlapping wrongly
  //  - double-buffered rendering to avoid tearing/flickering
  glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB |
                                     Gdk::GL::MODE_DEPTH |
                                     Gdk::GL::MODE_DOUBLE);
  if (glconfig == 0) {
    // If we can't get this configuration, die
    std::cerr << "Unable to setup OpenGL Configuration!" << std::endl;
    abort();
  }

  // Accept the configuration
  set_gl_capability(glconfig);

  // Register the fact that we want to receive these events
  add_events(Gdk::BUTTON1_MOTION_MASK    |
             Gdk::BUTTON2_MOTION_MASK    |
             Gdk::BUTTON3_MOTION_MASK    |
             Gdk::BUTTON_PRESS_MASK      | 
             Gdk::BUTTON_RELEASE_MASK    |
             Gdk::POINTER_MOTION_MASK    |
             Gdk::VISIBILITY_NOTIFY_MASK);
    // initialize
    initialize();
    
    // start the game with timeout
    connection = Glib::signal_timeout().connect( 
                sigc::mem_fun(*this, &Viewer::start), current_speed );
}

Viewer::~Viewer()
{
  // Nothing to do here right now.
}

void Viewer::initialize()
{
    // mouse buttons
    button1 = false;
    button2 = false;
    button3 = false;
    
    // rotate & scale
    scale_mode = false;
    alpha = 0;
    theta = 0;
    gammar = 0;
	m_scale = 1.0;
    
    // mode
    draw_mode = WIREFRAME;    
    // speed
    speed = SLOW;
    current_speed = speed;
    // buffer
    double_buffer = false;
    // spin
    spin_velocity = 0.0;
    // score
    score = 0;
    accum = 0;
    // bevel
    m_bevel = false;
    //light
    m_light = false;
    // not lose
    m_lose = false;
    animate_lose_counter = 0;
    // not pause
    m_pause = false;
}

void Viewer::reset()
{
    // rotate & scale
    scale_mode = false;
    alpha = 0;
    theta = 0;
    gammar = 0;
	m_scale = 1.0;
    // stop spining
    spin_velocity = 0.0;
    invalidate();
}

void Viewer::restart()
{
    m_lose = false;
    m_pause = false;
    animate_lose_counter = 0;
    current_speed = speed;
    score = 0;
    accum = 0;
    myGame.reset();
    if(connection)
        connection.disconnect();
    connection = Glib::signal_timeout().connect( 
      sigc::mem_fun(*this, &Viewer::start), current_speed );
    invalidate();
}

bool Viewer::start()
{
    int result = myGame.tick();
	if(result == -1){
        m_lose = true;
        connection.disconnect();
        lose_connection = Glib::signal_timeout().connect( 
            sigc::mem_fun(*this, &Viewer::loseAnimate), 50 );
    }
    else{
        score += result * 10;
        accum += result * 10;
        if (accum >= ACCUM_BOUND){
            accum = ACCUM_BOUND - accum;
            current_speed = current_speed * SPEEDFACTOR;
            connection.disconnect();
            connection = Glib::signal_timeout().connect( 
              sigc::mem_fun(*this, &Viewer::start), current_speed );
        }
    }
    invalidate();
    return true;
}

bool Viewer::loseAnimate()
{
    if ( animate_lose_counter >= 24 ){
        lose_connection.disconnect();
        return true;
    }
    invalidate();
    animate_lose_counter++;
    return true;
}

void Viewer::invalidate()
{
  //Force a rerender
  Gtk::Allocation allocation = get_allocation();
  get_window()->invalidate_rect( allocation, false);
  
}

void Viewer::on_realize()
{
  // Do some OpenGL setup.
  // First, let the base class do whatever it needs to
  Gtk::GL::DrawingArea::on_realize();
  
  Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();
  
  if (!gldrawable)
    return;

  if (!gldrawable->gl_begin(get_gl_context()))
    return;

  // Just enable depth testing and set the background colour.
  glEnable(GL_DEPTH_TEST);
  glClearColor(0.7, 0.7, 1.0, 0.0);
  
    setup_lighting();
    
  gldrawable->gl_end();
}

bool Viewer::on_expose_event(GdkEventExpose* event)
{
  Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();

  if (!gldrawable) return false;

  if (!gldrawable->gl_begin(get_gl_context()))
    return false;

  // Clear the screen

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    
  // Modify the current projection matrix so that we move the 
  // camera away from the origin.  We'll draw the game at the
  // origin, and we need to back up to see it.

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glTranslated(0.0, 0.0, -45.0);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
    
    if(m_light)
        glEnable(GL_LIGHTING);
    else
        glDisable(GL_LIGHTING);
    // draw score
    glPushMatrix();
    glDisable(GL_LIGHT0);
    glDisable(GL_LIGHT1);
    glDisable(GL_LIGHT2);
    glEnable(GL_LIGHT3);
    drawNumber(score, -2); // -2 is score color index
    glPopMatrix();
    
    // set up lighting (if necessary)
	glEnable(GL_LIGHT0);
    glEnable(GL_LIGHT1);
    glEnable(GL_LIGHT2);
    glDisable(GL_LIGHT3);
    // scale and rotate the scene
    glRotatef(alpha, 1.0, 0.0, 0.0);
    glRotatef(theta, 0.0, 1.0, 0.0);
    glRotatef(gammar, 0.0, 0.0, 1.0);
    glScalef(m_scale, m_scale, m_scale);
    
    // set polygon mode
    if( draw_mode == FACE || draw_mode == MULTICOLOURED )
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    else
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    
    // buffering
    if( double_buffer ) glDrawBuffer( GL_BACK );
    else glDrawBuffer( GL_FRONT );
    
    // draw well
    glPushMatrix();
    drawGameWell();
    glPopMatrix();
    
    // draw falling pieces
    glPushMatrix();
    drawFallingPieces();
    glPopMatrix();
    
  // We pushed a matrix onto the PROJECTION stack earlier, we 
  // need to pop it.
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();

  // Swap the contents of the front and back buffers so we see what we
  // just drew. This should only be done if double buffering is enabled.
  gldrawable->swap_buffers();

  gldrawable->gl_end();

  return true;
}

bool Viewer::on_configure_event(GdkEventConfigure* event)
{
  Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();

  if (!gldrawable) return false;
  
  if (!gldrawable->gl_begin(get_gl_context()))
    return false;

  // Set up perspective projection, using current size and aspect
  // ratio of display

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glViewport(0, 0, event->width, event->height);
  gluPerspective(40.0, (GLfloat)event->width/(GLfloat)event->height, 0.1, 1000.0);

  // Reset to modelview matrix mode
  
  glMatrixMode(GL_MODELVIEW);

  gldrawable->gl_end();

  return true;
}

void Viewer::drawGameWell()
{
    //starting at right top corner
    glTranslated(myGame.getWidth()/2+1, myGame.getHeight()/2, 0.0);
    // draw right well
    for(int i=0; i<=myGame.getHeight(); i++){
		glTranslated(0.0, -1.0, 0.0);
		drawCube(-1);
	}
	// draw bottom well
	for(int i=0; i<=myGame.getWidth(); i++){
		glTranslated(-1.0, 0.0, 0.0);
		drawCube(-1);
	}
    // draw left well
	for(int i=0; i<myGame.getHeight(); i++){
		glTranslated(0.0, +1.0, 0.0);
		drawCube(-1);
	}
}

void Viewer::drawFallingPieces()
{
    int row;
    if(m_lose) row = animate_lose_counter;
    else row = GAME_HEIGHT+4;
    // starting at left bottom of the well
    glTranslated(-GAME_WIDTH/2, -(GAME_HEIGHT/2+1), 0.0);
    for(int i=0; i<row; i++){
        glTranslated(0.0, 1.0, 0.0);
        for(int j=0; j<GAME_WIDTH; j++){
            glTranslated(1.0, 0.0, 0.0);
            int cindex = myGame.get(i,j);
            if(cindex != -1){
                drawCube(cindex);
            }
            else if(m_lose){
                drawCube(-1);
            }
        }
        glTranslated(-GAME_WIDTH, 0.0, 0.0);
    }
    
}

void Viewer::drawNumber(int num, int colorindex){
    glTranslated(0.0, 0.0, -100.0);
    glTranslated(25.0, 52.0 , 0.0);
    std::stringstream ss;
    ss << num;
    std::string num_string = ss.str();
    for( int i=num_string.size()-1; i >= 0; i-- ){
        glTranslated(-4.0, 0.0, 0.0);
        char ch = num_string[i];
        if (ch == '1'){
            glPushMatrix();
            glTranslated(1.0, 0.0, 0.0);
            for( int j=0; j<5; j++){
                drawCube(colorindex);
                glTranslated(0.0, -1.0, 0.0);
            }
            glTranslated(0.0, 5.0, 0.0);
            glPopMatrix();
        }
        else if (ch == '2'){
            glPushMatrix();
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glPopMatrix();
        }
        else if (ch == '3'){
            glPushMatrix();
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(2.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glPopMatrix();
        }
        else if (ch == '4'){
            glPushMatrix();
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 2.0, 0.0);
            for(int j=0; j<5; j++){
                drawCube(colorindex);
                glTranslated(0.0, -1.0, 0.0);
            }
            glPopMatrix();
        }
        else if (ch == '5'){
            glPushMatrix();
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(-2.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glPopMatrix();
        }
        else if (ch == '6'){
            glPushMatrix();
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(-2.0, 0.0, 0.0);
            for(int j=0; j<5; j++){
                drawCube(colorindex);
                glTranslated(0.0, -1.0, 0.0);
            }
            glTranslated(1.0, 1.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, 1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, 1.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glPopMatrix();
        }
        else if (ch == '7'){
            glPushMatrix();
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, 1.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            for(int j=0; j<5; j++){
                drawCube(colorindex);
                glTranslated(0.0, -1.0, 0.0);
            }
            glPopMatrix();
        }
        else if (ch == '8'){
            glPushMatrix();
            for(int j=0; j<5; j++){
                drawCube(colorindex);
                glTranslated(2.0, 0.0, 0.0);
                drawCube(colorindex);
                glTranslated(-2.0, -1.0, 0.0);
            }
            glTranslated(1.0, 1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, 2.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, 2.0, 0.0);
            drawCube(colorindex);
            glPopMatrix();
        }
        else if (ch == '9'){
            glPushMatrix();
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, -1.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 0.0, 0.0);
            drawCube(colorindex);
            glTranslated(1.0, 2.0, 0.0);
            for(int j=0; j<5; j++){
                drawCube(colorindex);
                glTranslated(0.0, -1.0, 0.0);
            }
            glTranslated(-1.0, 1.0, 0.0);
            drawCube(colorindex);
            glTranslated(-1.0, 0.0, 0.0);
            drawCube(colorindex);
            
            glPopMatrix();
        }
        else if (ch == '0'){
            glPushMatrix();
            for(int j=0; j<5; j++){
                drawCube(colorindex);
                glTranslated(2.0, 0.0, 0.0);
                drawCube(colorindex);
                glTranslated(-2.0, -1.0, 0.0);
            }
            glTranslated(1.0, 1.0, 0.0);
            drawCube(colorindex);
            glTranslated(0.0, 4.0, 0.0);
            drawCube(colorindex);
            glPopMatrix();
        }
    }
}

// set drawing color
void Viewer::set_color(myColor c)
{
    switch (c) {
        case FIREBRICK:
            glColor4d(178.0/255, 34.0/255, 34.0/255, 1.0);
            break;    
        case GREY:
            glColor4d(84.0/255, 84.0/255, 84.0/255, 1.0);
            break;
        case BLACK:
            glColor4d(0.0/255, 0.0/255, 0.0/255, 1.0);
            break;
        case BROWN:
            glColor4d(165.0/255, 42.0/255, 42.0/255, 1.0);
            break;
        case BLUE:
            glColor4d(138.0/255, 43.0/255, 226.0/255, 1.0);
            break;
        case GREEN:
            glColor4d(34.0/255, 139.0/255, 34.0/255, 1.0);
            break;
        case RED:
            glColor4d(192.0/255, 0.0/255, 0.0/255, 1.0);
            break;
        case PURPLE:
            glColor4d(191.0/255, 62.0/255, 255.0/255, 1.0);
            break;
        case YELLOW:
            glColor4d(255.0/255, 255.0/255, 0.0/255, 1.0);
            break;
        case ORANGE:
            glColor4d(255.0/255, 140.0/255, 0.0/255, 1.0);
            break;
        default:
            break;
    }
}


// draw an unit cube
void Viewer::drawCube(int part)
{
    if (part == -2){
        set_color(FIREBRICK);
        topFace();
        frontFace();
        rightFace();
        leftFace();
        bottomFace();
        backFace();
    }
    else if (part == -1){
        set_color(GREY);
        frontFace();
		if(draw_mode == MULTICOLOURED) set_color(BLUE);
		topFace();
		if(draw_mode == MULTICOLOURED) set_color(GREEN);
        rightFace();
		if(draw_mode == MULTICOLOURED) set_color(BROWN);
        leftFace();
		if(draw_mode == MULTICOLOURED) set_color(PURPLE);
        bottomFace();
		if(draw_mode == MULTICOLOURED) set_color(RED);
        backFace();
    }
    else if (part == 0){
        set_color(RED);
        topFace();
        if(draw_mode == MULTICOLOURED) set_color(BLUE);
        frontFace();
        if(draw_mode == MULTICOLOURED) set_color(BROWN);
        rightFace();
        if(draw_mode == MULTICOLOURED) set_color(GREEN);
        leftFace();
        if(draw_mode == MULTICOLOURED) set_color(ORANGE);
        bottomFace();
        if(draw_mode == MULTICOLOURED) set_color(PURPLE);
        backFace();
    }
    else if (part == 1){
        set_color(BROWN);
        topFace();
        if(draw_mode == MULTICOLOURED) set_color(BLUE);
        frontFace();
        if(draw_mode == MULTICOLOURED) set_color(RED);
        rightFace();
        if(draw_mode == MULTICOLOURED) set_color(YELLOW);
        leftFace();
        if(draw_mode == MULTICOLOURED) set_color(PURPLE);
        bottomFace();
        if(draw_mode == MULTICOLOURED) set_color(GREEN);
        backFace();
    }
    else if (part == 2){
        set_color(BLUE);
        topFace();
        if(draw_mode == MULTICOLOURED) set_color(GREEN);
        frontFace();
        if(draw_mode == MULTICOLOURED) set_color(PURPLE);
        rightFace();
        if(draw_mode == MULTICOLOURED) set_color(RED);
        leftFace();
        if(draw_mode == MULTICOLOURED) set_color(BROWN);
        bottomFace();
        if(draw_mode == MULTICOLOURED) set_color(YELLOW);
        backFace();
    }
    else if (part == 3){
        set_color(GREEN);
        topFace();
        if(draw_mode == MULTICOLOURED) set_color(YELLOW);
        frontFace();
        if(draw_mode == MULTICOLOURED) set_color(BLUE);
        rightFace();
        if(draw_mode == MULTICOLOURED) set_color(BROWN);
        leftFace();
        if(draw_mode == MULTICOLOURED) set_color(GREEN);
        bottomFace();
        if(draw_mode == MULTICOLOURED) set_color(RED);
        backFace();
    }
    else if (part == 4){
        set_color(PURPLE);
        topFace();
        if(draw_mode == MULTICOLOURED) set_color(RED);
        frontFace();
        if(draw_mode == MULTICOLOURED) set_color(BLUE);
        rightFace();
        if(draw_mode == MULTICOLOURED) set_color(BROWN);
        leftFace();
        if(draw_mode == MULTICOLOURED) set_color(YELLOW);
        bottomFace();
        if(draw_mode == MULTICOLOURED) set_color(GREEN);
        backFace();
    }
    else if (part == 5){
        set_color(YELLOW);
        topFace();
        if(draw_mode == MULTICOLOURED) set_color(GREEN);
        frontFace();
        if(draw_mode == MULTICOLOURED) set_color(YELLOW);
        rightFace();
        if(draw_mode == MULTICOLOURED) set_color(BLUE);
        leftFace();
        if(draw_mode == MULTICOLOURED) set_color(BROWN);
        bottomFace();
        if(draw_mode == MULTICOLOURED) set_color(RED);
        backFace();
    }
    else if (part == 6){
        set_color(ORANGE);
        topFace();
        if(draw_mode == MULTICOLOURED) set_color(RED);
        frontFace();
        if(draw_mode == MULTICOLOURED) set_color(BLUE);
        rightFace();
        if(draw_mode == MULTICOLOURED) set_color(GREEN);
        leftFace();
        if(draw_mode == MULTICOLOURED) set_color(YELLOW);
        bottomFace();
        if(draw_mode == MULTICOLOURED) set_color(BLUE);
        backFace();
    }
}

void Viewer::topFace()
{
	/*This is the top face*/
    if (!m_bevel){
        glBegin(GL_POLYGON);
        glNormal3f(0.0f, 1.0f, 0.0f);
        glVertex3f(0.0f, 0.0f, 0.0f);
        glVertex3f(0.0f, 0.0f, -1.0f);
        glVertex3f(-1.0f, 0.0f, -1.0f);
        glVertex3f(-1.0f, 0.0f, 0.0f);
        glEnd();
    }
    else{
        glBegin(GL_POLYGON);
        glNormal3f(0.0f, 1.0f, 0.0f);
        glVertex3f(-0.1f, 0.0f, -0.1f);
        glVertex3f(-0.1f, 0.0f, -0.9f);
        glVertex3f(-0.9f, 0.0f, -0.9f);
        glVertex3f(-0.9f, 0.0f, -0.1f);
        glEnd();
        
        glBegin(GL_POLYGON);
        glNormal3f(1.0f, 1.0f, 1.0f);
        glVertex3f(-0.1f, 0.0f, -0.1f);
        glVertex3f(-0.1f, -0.1f, 0.0f);
        glVertex3f(0.0f, -0.1f, -0.1f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(1.0f, 1.0f, -1.0f);
        glVertex3f(-0.1f, 0.0f, -0.9f);
        glVertex3f(0.0f, -0.1f, -0.9f);
        glVertex3f(-0.1f, -0.1f, -1.0f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(-1.0f, 1.0f, -1.0f);
        glVertex3f(-0.9f, 0.0f, -0.9f);
        glVertex3f(-0.9f, -0.1f, -1.0f);
        glVertex3f(-1.0f, -0.1f, -0.9f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(-1.0f, 1.0f, 1.0f);
        glVertex3f(-0.9f, 0.0f, -0.1f);
        glVertex3f(-0.9f, -0.1f, 0.0f);
        glVertex3f(-1.0f, -0.1f, -0.1f);
        glEnd();
        
        glBegin(GL_POLYGON);
        glNormal3f(0.0f, 1.0f, 1.0f);
        glVertex3f(-0.1f, 0.0f, -0.1f);
        glVertex3f(-0.9f, 0.0f, -0.1f);
        glVertex3f(-0.9f, -0.1f, 0.0f);
        glVertex3f(-0.1f, -0.1f, 0.0f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(1.0f, 1.0f, 0.0f);
        glVertex3f(-0.1f, 0.0f, -0.9f);
        glVertex3f(-0.1f, 0.0f, -0.1f);
        glVertex3f(0.0f, -0.1f, -0.1f);
        glVertex3f(0.0f, -0.1f, -0.9f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(0.0f, 1.0f, -1.0f);
        glVertex3f(-0.9f, 0.0f, -0.9f);
        glVertex3f(-0.1f, 0.0f, -0.9f);
        glVertex3f(-0.1f, -0.1f, -1.0f);
        glVertex3f(-0.9f, -0.1f, -1.0f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(-1.0f, 1.0f, 0.0f);
        glVertex3f(-0.9f, 0.0f, -0.1f);
        glVertex3f(-0.9f, 0.0f, -0.9f);
        glVertex3f(-1.0f, -0.1f, -0.9f);
        glVertex3f(-1.0f, -0.1f, -0.1f);
        glEnd();
    }
}

void Viewer::frontFace()
{
    glBegin(GL_POLYGON);
	/*This is the front face*/
    glNormal3f(0.0f, 0.0f, 1.0f);
	if (!m_bevel){
        glVertex3f(0.0f, 0.0f, 0.0f);
        glVertex3f(-1.0f, 0.0f, 0.0f);
        glVertex3f(-1.0f, -1.0f, 0.0f);
        glVertex3f(0.0f, -1.0f, 0.0f);
    }
    else{
        glVertex3f(-0.1f, -0.1f, 0.0f);
        glVertex3f(-0.9f, -0.1f, 0.0f);
        glVertex3f(-0.9f, -0.9f, 0.0f);
        glVertex3f(-0.1f, -0.9f, 0.0f);
    }
    glEnd();
}
void Viewer::rightFace()
{
    glBegin(GL_POLYGON);
	/*This is the right face*/
	glNormal3f(1.0f, 0.0f, 0.0f);
	if (!m_bevel){
        glVertex3f(0.0f, 0.0f, 0.0f);
        glVertex3f(0.0f, -1.0f, 0.0f);
        glVertex3f(0.0f, -1.0f, -1.0f);
        glVertex3f(0.0f, 0.0f, -1.0f);
    }
    else{
        glVertex3f(0.0f, -0.1f, -0.1f);
        glVertex3f(0.0f, -0.9f, -0.1f);
        glVertex3f(0.0f, -0.9f, -0.9f);
        glVertex3f(0.0f, -0.1f, -0.9f);
    }
    glEnd();
}

void Viewer::leftFace()
{
    glBegin(GL_POLYGON);
	/*This is the left face*/
	glNormal3f(-1.0f, 0.0f, 0.0f);
    if (!m_bevel){
        glVertex3f(-1.0f, 0.0f, 0.0f);
        glVertex3f(-1.0f, 0.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, 0.0f);
    }
    else{
        glVertex3f(-1.0f, -0.1f, -0.1f);
        glVertex3f(-1.0f, -0.1f, -0.9f);
        glVertex3f(-1.0f, -0.9f, -0.9f);
        glVertex3f(-1.0f, -0.9f, -0.1f);
    }
    glEnd();
}

void Viewer::bottomFace()
{
	/*This is the bottom face*/
	if (!m_bevel){
        glBegin(GL_POLYGON);
        glNormal3f(0.0f, -1.0f, 0.0f);
        glVertex3f(0.0f, -1.0f, 0.0f);
        glVertex3f(0.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, 0.0f);
        glEnd();
    }
    else{
        glBegin(GL_POLYGON);
        glNormal3f(0.0f, -1.0f, 0.0f);
        glVertex3f(-0.1f, -1.0f, -0.1f);
        glVertex3f(-0.1f, -1.0f, -0.9f);
        glVertex3f(-0.9f, -1.0f, -0.9f);
        glVertex3f(-0.9f, -1.0f, -0.1f);
        glEnd();
        
        glBegin(GL_POLYGON);
        glNormal3f(1.0f, -1.0f, 1.0f);
        glVertex3f(-0.1f, -1.0f, -0.1f);
        glVertex3f(0.0f, -0.9f, -0.1f);
        glVertex3f(-0.1f, -0.9f, 0.0f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(1.0f, -1.0f, -1.0f);
        glVertex3f(-0.1f, -1.0f, -0.9f);
        glVertex3f(0.0f, -0.9f, -0.9f);
        glVertex3f(-0.1f, -0.9f, -1.0f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-0.9f, -1.0f, -0.9f);
        glVertex3f(-0.9f, -0.9f, -1.0f);
        glVertex3f(-1.0f, -0.9f, -0.9f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(-1.0f, -1.0f, 1.0f);
        glVertex3f(-0.9f, -1.0f, -0.1f);
        glVertex3f(-0.9f, -0.9f, 0.0f);
        glVertex3f(-1.0f, -0.9f, -0.1f);
        glEnd();
        
        glBegin(GL_POLYGON);
        glNormal3f(0.0f, -1.0f, 1.0f);
        glVertex3f(-0.9f, -1.0f, -0.1f);
        glVertex3f(-0.1f, -1.0f, -0.1f);
        glVertex3f(-0.1f, -0.9f, 0.0f);
        glVertex3f(-0.9f, -0.9f, 0.0f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(-1.0f, -1.0f, 0.0f);
        glVertex3f(-0.9f, -1.0f, -0.1f);
        glVertex3f(-0.9f, -1.0f, -0.9f);
        glVertex3f(-1.0f, -0.9f, -0.9f);
        glVertex3f(-1.0f, -0.9f, -0.1f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(0.0f, -1.0f, -1.0f);
        glVertex3f(-0.9f, -1.0f, -0.9f);
        glVertex3f(-0.1f, -1.0f, -0.9f);
        glVertex3f(-0.1f, -0.9f, -1.0f);
        glVertex3f(-0.9f, -0.9f, -1.0f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(1.0f, -1.0f, 0.0f);
        glVertex3f(-0.1f, -1.0f, -0.9f);
        glVertex3f(-0.1f, -1.0f, -0.1f);
        glVertex3f(0.0f, -0.9f, -0.1f);
        glVertex3f(0.0f, -0.9f, -0.9f);
        glEnd();
        
        glBegin(GL_POLYGON);
        glNormal3f(1.0f, 0.0f, 1.0f);
        glVertex3f(-0.1f, -0.1f, -0.0f);
        glVertex3f(-0.1f, -0.9f, -0.0f);
        glVertex3f(0.0f, -0.9f, -0.1f);
        glVertex3f(0.0f, -0.1f, -0.1f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(1.0f, 0.0f, -1.0f);
        glVertex3f(0.0f, -0.1f, -0.9f);
        glVertex3f(0.0f, -0.9f, -0.9f);
        glVertex3f(-0.1f, -0.9f, -1.0f);
        glVertex3f(-0.1f, -0.1f, -1.0f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(-1.0f, 0.0f, -1.0f);
        glVertex3f(-0.9f, -0.1f, -1.0f);
        glVertex3f(-0.9f, -0.9f, -1.0f);
        glVertex3f(-1.0f, -0.9f, -0.9f);
        glVertex3f(-1.0f, -0.1f, -0.9f);
        glEnd();
        glBegin(GL_POLYGON);
        glNormal3f(-1.0f, 0.0f, 1.0f);
        glVertex3f(-1.0f, -0.1f, -0.1f);
        glVertex3f(-1.0f, -0.9f, -0.1f);
        glVertex3f(-0.9f, -0.9f, 0.0f);
        glVertex3f(-0.9f, -0.1f, 0.0f);
        glEnd();
    }
}

void Viewer::backFace()
{
    glBegin(GL_POLYGON);
	/*This is the back face*/
	glNormal3f(0.0f, 0.0f, -1.0f);
    if (!m_bevel){
        glVertex3f(0.0f, 0.0f, -1.0f);
        glVertex3f(-1.0f, 0.0f, -1.0f);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(0.0f, -1.0f, -1.0f);
    }
    else{
        glVertex3f(-0.1f, -0.1f, -1.0f);
        glVertex3f(-0.9f, -0.1f, -1.0f);
        glVertex3f(-0.9f, -0.9f, -1.0f);
        glVertex3f(-0.1f, -0.9f, -1.0f);
    }
    glEnd();
}

// game control LEFT
void Viewer::moveLeft()
{
    if(m_lose || m_pause) return;
	myGame.moveLeft();
	invalidate();
}
// game control RIGHT
void Viewer::moveRight()
{
    if(m_lose || m_pause) return;
	myGame.moveRight();
	invalidate();
}
// game control DROP
void Viewer::drop()
{
    if(m_lose || m_pause) return;
	myGame.drop();
	invalidate();
}
// game control ROTATE CLOCKWISE
void Viewer::rotateCW()
{
    if(m_lose || m_pause) return;
	myGame.rotateCW();
	invalidate();
}
// game control ROTATE COUNTER CLOCKWISE
void Viewer::rotateCCW()
{
    if(m_lose || m_pause) return;
	myGame.rotateCCW();
	invalidate();
}

void Viewer::set_light()
{
    if(m_light)
        m_light = false;
    else
        m_light = true;
    invalidate();
}

void Viewer::set_bevel()
{
    if(m_bevel)
        m_bevel = false;
    else
        m_bevel = true;
    invalidate();
}

void Viewer::pause()
{
    if (connection){
        connection.disconnect();
        m_pause = true;
    }
    else{
        connection = Glib::signal_timeout().connect( 
                sigc::mem_fun(*this, &Viewer::start), current_speed );
        m_pause = false;
    }
}

// setup lighting
void Viewer::setup_lighting()
{
    // enable lighting
    glEnable(GL_LIGHTING);
    // enable color material
    glEnable(GL_COLOR_MATERIAL);
    
    glEnable(GL_NORMALIZE);
	//Add positioned lights
	GLfloat light[] = {1.0, 1.0, 1.0, 1.0};
	GLfloat position0[] = {0.0f, 0.0f, 0.0f, 1.0f};
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light);
	glLightfv(GL_LIGHT0, GL_POSITION, position0);
    
    GLfloat position1[] = {0.0f, 7.0f, 5.0f, 1.0f};
    glLightfv(GL_LIGHT1, GL_DIFFUSE, light);
	glLightfv(GL_LIGHT1, GL_POSITION, position1);
    
    GLfloat position2[] = {0.0f, -5.0f, 5.0f, 1.0f};
    glLightfv(GL_LIGHT2, GL_DIFFUSE, light);
	glLightfv(GL_LIGHT2, GL_POSITION, position2);
    
    GLfloat position3[] = {40.0f, 50.0f, 0.0f, 1.0f};
    glLightfv(GL_LIGHT3, GL_DIFFUSE, light);
	glLightfv(GL_LIGHT3, GL_POSITION, position3);
}

// set scale mode
void Viewer::set_scale_mode(bool m)
{
    scale_mode = m;
}

void Viewer::set_draw_mode(myDrawMode m)
{
    draw_mode = m;
    invalidate();
}

void Viewer::set_speed(mySpeed s)
{
    speed = s;
    current_speed = s;
    connection.disconnect();
    connection = Glib::signal_timeout().connect( 
                sigc::mem_fun(*this, &Viewer::start), current_speed );
}
void Viewer::set_double_buffer()
{
    if( double_buffer )
        double_buffer = false;
    else
        double_buffer = true;
}

bool Viewer::spin()
{
    if (spin_release_button1){
	alpha += spin_velocity;
	if(alpha >= 360.0)
		alpha -= 360.0;
    }
    if (spin_release_button2){
    	theta += spin_velocity;
	if(theta >= 360.0)
		theta -= 360.0;
    }
    if (spin_release_button3){
	gammar += spin_velocity;
	if(gammar >= 360.0)
		gammar -= 360.0;
    }
    invalidate();
    return true;
}

bool Viewer::on_button_press_event(GdkEventButton* event)
{
    init_point = Point2D(event->x, event->y);
    switch(event->button){
        case 1:
            button1 = true;
            break;
        case 2:
            button2 = true;
            break;
        case 3:
            button3 = true;
            break;
        default:
            break;
    }
    if(!scale_mode){
        spin_connection.disconnect();
        spin_release_button1 = false;
	spin_release_button2 = false;
	spin_release_button3 = false;
        spin_velocity = 0.0;
    }
  return true;
}

bool Viewer::on_button_release_event(GdkEventButton* event)
{
    switch(event->button){
        case 1:
            button1 = false;
	    spin_release_button1 = true;
            break;
        case 2:
            button2 = false;
	    spin_release_button2 = true;
            break;
        case 3:
            button3 = false;
	    spin_release_button3 = true;
            break;
        default:
            break;
    }
    if(!scale_mode){
        spin_connection = Glib::signal_timeout().connect( 
                        sigc::mem_fun(*this, &Viewer::spin), 50 );
    }
  return true;
}

bool Viewer::on_motion_notify_event(GdkEventMotion* event)
{
    int delta_x = event->x - init_point[0];
    if(button1){
	if(!scale_mode)
        	alpha += delta_x/3.0;
    }
    if(button2){
	if(!scale_mode)
    		theta += delta_x/3.0;
    }
    if(button3){
	if(!scale_mode)
    		gammar += delta_x/3.0;
    }

    if(button1 || button2 || button3){
        if (scale_mode){
            m_scale += (-delta_x)/100.0;
            if (m_scale >= MAX_SCALE)
                m_scale = MAX_SCALE;
            else if (m_scale <= MIN_SCALE)
                m_scale = MIN_SCALE;
        }
        spin_velocity = (event->x - init_point[0])/2;
    }
    invalidate();
    init_point = Point2D(event->x, event->y);
  return true;
}
